package uitl

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"time"

	jsoniter "github.com/json-iterator/go"
	"github.com/kataras/iris/v12"
	"github.com/qf-tech/qftech-base/pkg/log"
	qflog "github.com/qf-tech/qftech-base/pkg/log"
)

// TransJsonReq2Struct def
func TransJsonReq2Struct(ctx iris.Context, v interface{}) error {
	log := log.GetLogger().GetCtx(ctx.Request().Context()).Sugar()

	// 判断消息体是否是application/json
	isJSON := false
	for _, v := range ctx.Request().Header["Content-Type"] {
		isJSON = strings.Contains(v, "application/json")
	}

	if !isJSON {
		return errors.New("request content type is not json")
	}

	tmpData, err := ioutil.ReadAll(ctx.Request().Body)
	if err != nil {
		return err
	}
	reqData := string(tmpData)

	err = jsoniter.UnmarshalFromString(reqData, v)
	if err != nil {
		return err
	}

	log.Warnf("req uri: %s method: %s, req data: %s", ctx.Request().RequestURI, ctx.Request().Method, reqData)
	return nil
}

func CalculateHash(toBeHashed string) string {
	hashInBytes := sha256.Sum256([]byte(toBeHashed))
	hashStr := hex.EncodeToString(hashInBytes[:])
	return hashStr
}

func GenWsse(username string, password string, nonce string) string {
	created := time.Now().Format("2006-01-02T15:04:05Z")
	plainData := fmt.Sprintf("%s%s%s%s", username, password, nonce, created)
	digest := CalculateHash(plainData)

	wsseInfo := fmt.Sprintf("UsernameToken Username=\"%s\", PasswordDigest=\"%s\", Nonce=\"%s\", Created=\"%s\"",
		username, digest, nonce, created)
	return wsseInfo
}

func HttpPostJson(url string, reqBody interface{}, headers map[string]string, timeout time.Duration) (interface{}, error) {
	c := &http.Client{Timeout: timeout}

	bytesData, err := json.Marshal(reqBody)
	if err != nil {
		fmt.Println(err.Error())
		return nil, err
	}
	reader := bytes.NewReader(bytesData)
	req, err := http.NewRequest("POST", url, reader)
	if err != nil {
		return nil, err
	}

	for k, v := range headers {
		req.Header.Add(k, v)
	}

	resp, err := c.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var v interface{}
	err = json.Unmarshal(body, &v)
	if err != nil {
		return nil, err
	}

	return v, nil
}

func HttpPostFile(filename string, targetURL string, headers map[string]string, timeout time.Duration) (interface{}, error) {
	qflog.Sugare.Infof("file: %s, url: %s, headers: %v", filename, targetURL, headers)
	bodyBuf := bytes.NewBufferString("")
	bodyWriter := multipart.NewWriter(bodyBuf)

	// use the bodyWriter to write the Part headers to the buffer
	_, err := bodyWriter.CreateFormFile("file", filename)
	if err != nil {
		qflog.Sugare.Error("error writing to buffer")
		return nil, err
	}

	// the file data will be the second part of the body
	fh, err := os.Open(filename)
	if err != nil {
		qflog.Sugare.Error("error opening file")
		return nil, err
	}
	// need to know the boundary to properly close the part myself.
	boundary := bodyWriter.Boundary()
	//close_string := fmt.Sprintf("\r\n--%s--\r\n", boundary)
	closeBuf := bytes.NewBufferString(fmt.Sprintf("\r\n--%s--\r\n", boundary))

	// use multi-reader to defer the reading of the file data until
	// writing to the socket buffer.
	requestReader := io.MultiReader(bodyBuf, fh, closeBuf)
	fi, err := fh.Stat()
	if err != nil {
		qflog.Sugare.Error("Error Stating file: %s", filename)
		return nil, err
	}

	req, err := http.NewRequest("POST", targetURL, requestReader)
	if err != nil {
		qflog.Sugare.Error("new request err: %v", err)
		return nil, err
	}

	// Set headers for multipart, and Content Length
	req.Header.Add("Content-Type", "multipart/form-data; boundary="+boundary)
	for k, v := range headers {
		req.Header.Add(k, v)
	}
	req.ContentLength = fi.Size() + int64(bodyBuf.Len()) + int64(closeBuf.Len())

	c := &http.Client{Timeout: timeout}
	resp, err := c.Do(req)
	if err != nil {
		qflog.Sugare.Error("do request err: %v", err)
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		qflog.Sugare.Error("read all from response err: %v", err)
		return nil, err
	}

	var v interface{}
	err = json.Unmarshal(body, &v)
	if err != nil {
		qflog.Sugare.Error("json unmarshal err: %v", err)
		return nil, err
	}

	return v, nil
}
